package com.sencorsta.ids.core.application;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import com.alibaba.fastjson.JSONObject;
import com.sencorsta.ids.core.application.master.MasterClient;
import com.sencorsta.ids.core.application.proxy.ProxyClient;
import com.sencorsta.ids.core.configure.*;
import com.sencorsta.ids.core.data.mysql.DBPoolConnection;
import com.sencorsta.ids.core.entity.ClientEvent;
import com.sencorsta.ids.core.function.FunctionSystem;
import com.sencorsta.ids.core.function.SearchHandlers;
import com.sencorsta.ids.core.log.Out;
import com.sencorsta.ids.core.tcp.http.HttpHandler;
import com.sencorsta.ids.core.tcp.http.HttpServer;
import com.sencorsta.ids.core.tcp.opensocket.OpenMessage;
import com.sencorsta.ids.core.tcp.opensocket.OpenServerBootstrap;
import com.sencorsta.ids.core.tcp.opensocket.OpenUser;
import com.sencorsta.ids.core.tcp.socket.NetHandler;
import com.sencorsta.ids.core.tcp.socket.protocol.RpcMessage;
import com.sencorsta.ids.core.tcp.socket.server.RpcServerBootstrap;
import com.sencorsta.utils.string.StringUtil;

import io.netty.channel.Channel;

public class Application {
    /**
     * 是否测试环境
     */
    public static boolean DEBUG;
    //全局时间
    public static volatile long APP_TIME = System.currentTimeMillis();
    /**
     * 应用类型
     */
    public static String SERVER_TYPE;

    /**
     * 单例
     */
    protected static Application instance;

    public static HttpServer Web = new HttpServer();

    AppDispatcher dispatcher;

    // protected final Map<String, RpcMessageDispatcher> globalDispatchers = new
    // HashMap<String, RpcMessageDispatcher>();

    public Application() {
    }

    /**
     * 获取单例
     */
    public static Application getInstance() {
        if (instance == null) {
            synchronized (Application.class) {
                if (instance == null) {
                    instance = new Application();
                }
            }
        }
        return instance;
    }

    /**
     * 启动
     */
    public void start(String name) {
        try {
            SysConfig.getInstance().init();
            DEBUG = SysConfig.getInstance().isEnableDebug();
            SERVER_TYPE = SysConfig.getInstance().get("server.type");

            dispatcher = new AppDispatcher(name + "Server");
            new Thread(dispatcher, name + ".dispatcher").start();

            initDispatcher();
            addCloseProcess();

            RpcServerBootstrap boot = new RpcServerBootstrap("RPC");
            boot.start();


            // 启动HTTP服务器
            httpStart(name);
            // 启动外部服务器
            openServerStart(name);
            // 启动Mysql服务器
            databaseStart(name);
            // 启动Master客户端
            masterStart(name);

            // 启动完成后调用 继承用
            onStarted();

            Out.info("操作系统环境", " -> ", FunctionSystem.OS_NAME);
            Out.info("CPU核心数：", " -> ", Runtime.getRuntime().availableProcessors());
            Out.info("内存大小：", " -> ", Runtime.getRuntime().freeMemory()/1024/1024,"MB(可用)/",Runtime.getRuntime().totalMemory()/1024/1024,"MB(最大)");
            Out.info("磁盘剩余空间：", " -> ", new File(System.getProperty("user.dir")).getFreeSpace()/1024/1024+"MB");



            Out.info("服务已成功启动运行喽，😺😸😹😻!", "\nPowered by IDS ©");

        } catch (Exception e) {
            Out.error(e.getMessage());
            e.printStackTrace();

            Out.warn("❌❌❌服务启动失败❌❌❌!", "系统将于3秒后关闭....");

            FunctionSystem.addFixJob(new Runnable() {
                int count = 3;

                @Override
                public void run() {
                    Out.warn(count + "...");
                    count--;
                }
            }, 0, 1000, 3);
            FunctionSystem.addDelayJob(new Runnable() {
                @Override
                public void run() {
                    Out.warn("⚠系统关闭⚠");
                    System.exit(0);
                }
            }, 3000);
        }
    }

    /**
     * 分发消息处理
     */
    public void addReceiveToDispatcher(RpcMessage message) {
        // Out.trace("Application添加新消息:" + message);
        dispatcher.addReceive(message);
    }


    public void addOpenMessage(OpenMessage messageIn) {
        //TODO 增加消息队列


        if (messageIn.method.indexOf('.') < 0) {
            Out.warn("消息格式错误：" + messageIn.method);
            return;
        }

        String type = messageIn.method.substring(0, messageIn.method.indexOf('.'));

        //判断是否是本机消息
        String localType = SysConfig.getInstance().get("server.type");

        String method = messageIn.method.substring(messageIn.method.indexOf('.') + 1);

        //判断是否无需登录接口
        //TODO 登录白名单机制
        boolean isUnLogin = false;
//        if (messageIn.method.equals(localType + ".ConnectByProto")) {
//            isUnLogin = true;
//        }
//        if (messageIn.method.equals(localType + ".ConnectByJson")) {
//            isUnLogin = true;
//        }

        //无需登陆的接口需要配置
        isUnLogin = SysConfig.getInstance().getBoolean("UnLogin."+localType+"."+ method, false);

        RpcMessage message = new RpcMessage(TypeProtocol.TYPE_RPC_REQ);
        message.method = method;
        message.serializeType = messageIn.serializeType;
        message.data = messageIn.data;

        if (isUnLogin) {
            message.channel = messageIn.channel;
            dispatcher.addReceive(message);
        } else {
            //获取userId
            OpenUser user = messageIn.channel.attr(OpenServerBootstrap.getInstance().KEY_OPENUSER).get();
            if (user == null || StringUtil.isEmpty(user.userId)) {
                Out.warn("用户未登录");
                messageIn.channel.closeFuture();
                return;
            }
            message.userId = user.userId;

            if (localType.equals(type)) {
                //本地消息
                dispatcher.addReceive(message);
            } else {
                //远程消息
                ProxyClient.sendByType(message, type);
            }
        }


    }

    public void sendOpenMessage(RpcMessage messageIn) {
        if (!OpenServerBootstrap.getInstance().isStarted()) {
            Out.debug("发送消息失败 open服务未开启!");
            return;
        }
        OpenUser user = OpenServerBootstrap.getInstance().users.get(messageIn.userId);
        if (user == null) {
            Out.debug("发送消息失败 用户为空：" + messageIn.userId + " (可能掉线，可能不在此服务器)");
            return;
        }

        OpenMessage message = new OpenMessage();
        message.method = messageIn.method;
        message.serializeType = messageIn.serializeType;
        message.data = messageIn.data;
        message.header.type = TypeProtocol.TYPE_RES;
        message.channel = user.channel;

        Out.debug("收到回调，准备发送给用户！" + message.toString());
        OpenServerBootstrap.getInstance().addMessage(message);
    }


//	public void addGlobalDispatcher(String method, RpcMessageDispatcher dispatcher) {
//		if (globalDispatchers.containsKey(method)) {
//			Out.warn("发现重复的dispatchers : ", method);
//		}
//		globalDispatchers.put(method, dispatcher);
//		// Out.trace("globalDispatchers添加:" + method);
//	}
//
//	public void putGlobalDispatcher(String method, RpcMessage message) {
//		RpcMessageDispatcher dispatcher = globalDispatchers.get(method);
//		if (dispatcher != null) {
//			dispatcher.addReceive(message);
//		} else {
//			Out.warn("未找到Global method处理类 : ", method == null ? "null" : method);
////			if (packet.getHeader() instanceof PomeloHeader) {
////				Channel session = packet.getSession();
////				PomeloResponse res = new PomeloResponse() {
////					@Override
////					protected void write() throws IOException {
////						body.writeBytes(ERROR_BUF);
////					}
////				};
////				res.getHeader().reqId = ((PomeloHeader) packet.getHeader()).reqId;
////				session.writeAndFlush(res.getContent());
////			}
//		}
//	}

    protected void initDispatcher() {
        String searchPath = searchPath();
        if (!StringUtil.isEmpty(searchPath)) {
            String[] paths = searchPath.split(";");
            for (String path : paths) {
                SearchHandlers.init(path);
            }
        } else {
            Out.warn("句柄搜索路径为空!");
        }
    }

    protected void httpStart(String name) {

        if (SysConfig.getInstance().getBoolean("http.enable", false)) {
            Web.echo();
            new Thread(() -> {
                try {
                    Web.run(SysConfig.getInstance().get("http.host", "0.0.0.0"),
                            SysConfig.getInstance().get("http.host.public", "0.0.0.0"),
                            SysConfig.getInstance().getInt("http.port", 80));
                } catch (Exception e) {
                    Out.trace("Http Server 启动失败! " + e.getMessage());
                    e.printStackTrace();
                }
            }, name + "Http").start();
        } else {
            Out.trace("HTTP服务器未开启");
        }

    }

    protected void openServerStart(String name) throws Exception {
        if (SysConfig.getInstance().getBoolean("openServer.enable", false)) {
            OpenServerBootstrap.getInstance().start();
        } else {
            Out.trace("openServer服务器未开启");
        }

    }

    protected void databaseStart(String name) throws Exception {
        if (SysConfig.getInstance().getBoolean("database.mysql.enable", false)) {
            HashMap<String, String> properties = new HashMap<>();
            properties.put("url", SysConfig.getInstance().get("database.mysql.url"));
            properties.put("username", SysConfig.getInstance().get("database.mysql.username"));
            properties.put("password", SysConfig.getInstance().get("database.mysql.password"));
            DBPoolConnection.getInstance().init(properties);
        } else {
            Out.trace("database服务未开启");
        }

    }

    protected void masterStart(String name) throws Exception {
        if (SysConfig.getInstance().getBoolean("master.client.enable", true)) {
            try {
                MasterClient.getInstance().start();
                return;
            } catch (Exception e) {
                throw new Exception("master启动失败,请检查master是否正常,配置是否填写正确");
            }

        } else {
            Out.trace("MasterClient服务未开启");
        }
    }

    protected String searchPath() {
        String res = "";
//		String searchPath = instance.getClass().getPackage().getName();
//		res += searchPath + ";";
        String fixdPath = "com.sencorsta.ids";
        res += fixdPath + ";";
        return res;
    }

    public void addClassBySearchPath(String className) {
        try {
            Class<?> clz = Class.forName(className);
            if (clz.isAnnotationPresent(ClientEvent.class)) {
                registerHandler((NetHandler) clz.getDeclaredConstructor().newInstance());
            }
//			if (clz.isAnnotationPresent(GGMEvent.class)) {
//				GMClient.getInstance().registerHandler((NetHandler) clz.newInstance());
//			}
            addClassByAnnotation(clz);
        } catch (Exception e) {
            Out.error(e);
        }
    }

    protected void addClassByAnnotation(Class<?> clz) throws Exception {
        if (clz.getSuperclass() == HttpHandler.class) {
            Web.addHandler((HttpHandler) clz.getDeclaredConstructor().newInstance());
        }
    }

    /**
     * 注册客户端请求处理句柄
     *
     * @param handler
     */
    public void registerHandler(NetHandler handler) {
        dispatcher.registerHandler(handler);
    }

    /**
     * 关闭程序时保存在线玩家的数据
     */
    protected void addCloseProcess() {
        Runtime.getRuntime().addShutdownHook(new Thread("程序维护") {
            public void run() {
                Out.info("正在进行安全停服中...");
                try {
                    if (dispatcher != null) {
                        dispatcher.stop();
                    }
                    // 通知维护
//					BroadcastMessage stopMsg = stopMessage();
//					if (stopMsg != null) {
//						for (GPlayer player : onlinePlayers.values()) {
//							player.receive(stopMsg);
//						}
//					}
                    onCloseGame();
                    // ProxyClient.onCloseGame();
                } finally {
                    try {
                        resourceRelease();
                    } finally {
//						while (GDao.size() > 0) {
//							Out.info(String.format("等待数据保存到数据库，还有【%d】条记录！", GDao.size()));
//							FunctionSystem.waitMills(500);
//						}
                    }
                }
                Out.info("服务器已安全停止，可以继续执行后续的工作了，O(∩_∩)O~");
            }
        });

        // 安全关服
        if (DEBUG && FunctionSystem.OS_NAME.startsWith("WINDOWS")) {
            Out.debug("注册停服事件", " -> ", "成功");
            new Thread(() -> {
                try {
                    System.in.read();
                } catch (IOException e) {
                    Out.error(e);
                }
                System.exit(0);
            }, "回车停服：测试启用").start();
        }

    }

    protected void onStarted() {
        Out.trace("‼请继承此方法‼ Started...");
    }

    protected void onCloseGame() {
        Out.trace("‼请继承此方法‼ close zone...");
    }

    protected void resourceRelease() {
        Out.trace("‼请继承此方法‼ resourceRelease...");
    }

    public void onServiceClose(Channel channel) {
        Out.trace("‼请继承此方法‼ onServiceClose...");
    }

    public void onOpenClose(Channel channel) {
        Out.trace("‼请继承此方法‼ onOpenClose...");
    }
    public void onUserLeave(String userId) {
        Out.trace("‼请继承此方法‼ onUserLeave...");
    }

}
